#include "FEMSolve.h"

#include "DataStructure/Common/Id.h"
#include "DataStructure/Common/Out.h"
#include "DataStructure/Input/Solver/AnaysisTypes.h"
#include "DataStructure/Input/Solver/Subcase.h"

#include "DBManager/IService/Input/Load/ISPCDService.h"
#include "DBManager/IService/Input/Subcase/ISubcaseService.h"

#include "SGFem/ADVEleCalculator/Register/AdvancedElementCalculatorRegister.h"
#include "SGFem/ConnectElement/Common/ConnectElementRegister.h"
#include "SGFem/ElementCalculator/Register/ElementCalculatorRegister.h"
#include "SGFem/PublicElement/Common/PublicElementCalculatorRegister.h"

// add ------------
#include "SGFem/ADVEleCalculator/ShellElement/Quad4Shell.h"
#include "SGFem/ElementCalculator/Factory/ElementCalculatorFactory.h"
// add ------------

#include "SGFem/Task/Static/StaticSolver.h"
#include "SGFem/Task/Thermal/ThermalSolver.h"
#include "SGFem/Task/Buckling/BucklingSolver.h"
#include "SGFem/Task/Modes/ModalSolver.h"
#include "SGFem/Task/Modes/ComplexModalSolver.h"
#include "SGFem/Task/Solver/FrequencyDirectSolver.h"
#include "SGFem/Task/Solver/FrequencyModeSolver.h"
#include "SGFem/Task/Solver/TransientDirectSolver.h"
#include "SGFem/Task/Solver/TransientModalSolver.h"

using SG::DataStructure::Common::IdArray;
using SG::DataStructure::FEM::ElementTypes;
using SG::DataStructure::FEM::SOLType;
using SG::DataStructure::FEM::Subcase;
using namespace SG::DBManager;

/** @brief 按照边界条件对所有工况进行分组
 *
 * 静力工况具有相同边界条件加入同一个分组
 */
static void splitSubcaseGroup (const DBServiceFactorySP dbServiceFactory, _OUT std::vector<IdArray>& subcaseGroup)
{
    subcaseGroup.clear ();
    std::vector<int> isSPCD;
    const auto&      subcaseService = dbServiceFactory->get<ISubcaseService> ();

    // 获取模型中所有的subCase
    auto subcaseIds = subcaseService->GetAllSubcaseID ();

    for (auto subcaseId : subcaseIds)
    {
        auto    para = subcaseService->FindSubCase (subcaseId);
        Subcase subcase (para);
        // 当前工况非静力或者使用了spcd,添加新分组，并跳出
        if (SOLType::SOL_SESTATIC != subcase.GetSOLType ())
        {
            subcaseGroup.push_back (IdArray{ subcaseId });
            isSPCD.push_back (0);
            continue;
        }

        // 当前工况使用强制位移
        SG::DataStructure::FEM::SPCDataArray spcdSet;
        dbServiceFactory->get<ISPCDService> ()->FindBySId (subcase.GetLoadId (), spcdSet);
        bool spcd = (0 != spcdSet.size ());
        if (spcd)
        {
            subcaseGroup.push_back (IdArray{ subcaseId });
            isSPCD.push_back (1);
            continue;
        }

        // 当前工况和其他工况是否具有相同边界条件
        bool sameBC      = false;
        int  curentGroup = 0;
        for (auto& group : subcaseGroup)
        {
            auto&   subcaseId0 = group[0];
            auto    para       = subcaseService->FindSubCase (subcaseId0);
            Subcase subcase0 (para);
            // 工况组group 非静力工况或者使用强制位移 跳过本分组，查询下一个分组
            if (SOLType::SOL_SESTATIC != subcase0.GetSOLType () || isSPCD[curentGroup])
            {
                continue;
            }

            // 进一步比较 当前工况subcase 与工况组subcaseGroup[curentGroup] 边界条件是否相同
            sameBC = (subcase0.GetSPCId () == subcase.GetSPCId () && subcase0.GetMPCId () == subcase.GetMPCId ());
            if (sameBC)
            {
                // 找到相同边界条件
                subcaseGroup[curentGroup].push_back (subcaseId);
                break;  // 跳出查找组，并完成处理。 此时不会产生新的组
            }

            curentGroup++;
        }

        // 没有找到边界相同的静力工况，subcaseId 自己构成一个新的静力工况分组
        if (!sameBC)
        {
            subcaseGroup.push_back (IdArray{ subcaseId });
            isSPCD.push_back (0);  // 后续工况可能和当前工况相同 (spcd 无法运行到此处)
        }
    }
}

/** @brief 注册单元计算类 */
void RegisterElementCalculator ()
{
    // 注册单元计算工厂 （非线性选项是否打开// 考虑非线性计算，可能调用其他的单元注册或者计算函数）
    SG::FEMSolver::Element::elementCalculatorRegister ();
    SG::FEMSolver::Element::publicElementCalculatorRegister ();
    SG::FEMSolver::Element::advancedElementCalculatorRegister ();
    SG::FEMSolver::Element::ConnectElementRegister ();
    SG::FEMSolver::Element::ElementCalculatorFactory::Register (ElementTypes::ELETYPE_ShellQuad4,
                                                                SG::FEMSolver::Element::Quad4Shell::Create);
}

void FEMSolve (SG::DBManager::DBServiceFactorySP dbManager, SG::DataStructure::FEM::Project project)
{
    // 注册单元计算类
    RegisterElementCalculator ();

    // 按照边界条件和分析类型对 工况进行分组
    // 边界条件相同的静力工况将被分组到一个工况中
    std::vector<SG::DataStructure::Common::IdArray> subcaseGroup;
    splitSubcaseGroup (dbManager, _OUT subcaseGroup);

    for (auto& group : subcaseGroup)
    {
        auto    subcaseId = group[0];
        auto    para      = dbManager->get<ISubcaseService> ()->FindSubCase (subcaseId);
        Subcase subcase (para);
        auto    solType = subcase.GetSOLType ();

        if (SOLType::SOL_SESTATIC == solType)
        {
            StaticSolver solver;
            solver.Solve (dbManager, project, group);
        }

        if (SOLType::SOL_SEBUCKL == solType)
        {
            BucklingSolver::Solve (dbManager, project, subcaseId);
        }

        if (SOLType::SOL_NLSCSH == solType)
        {
            ThermalSolver::Solve (dbManager, project, group);
        }

        if (SOLType::SOL_SEMODES == solType)
        {
            ModalSolver solver;
            solver.Solve (dbManager, project, subcaseId);
        }

        if (SOLType::SOL_SEDCEIG == solType)
        {
            ComplexModalSolver solver;
            solver.Solve (dbManager, project, subcaseId);
        }

        if (SOLType::SOL_SEDTRAN == solType)
        {
            TransientDirectSolver::Solve (dbManager, project, subcaseId);
        }

        if (SOLType::SOL_SEMTRAN == solType)
        {
            TransientModalSolver::Solve (dbManager, project, subcaseId);
        }

        if (SOLType::SOL_SEDFREQ == solType)
        {
            FrequencyDirectSolver::Solve (dbManager, project, subcaseId);
        }

        if (SOLType::SOL_SEMFREQ == solType)
        {
            FrequencyModeSolver::Solve (dbManager, project, subcaseId);
        }
    }
}